package org.zjvis.datascience.web.aspect;

import java.lang.annotation.Annotation;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.zjvis.datascience.common.annotation.CategoryAuth;
import org.zjvis.datascience.common.annotation.DatasetAuth;
import org.zjvis.datascience.common.annotation.PipelineAuth;
import org.zjvis.datascience.common.annotation.ProjectAuth;
import org.zjvis.datascience.common.annotation.ProjectRoleAuth;
import org.zjvis.datascience.common.annotation.TaskAuth;
import org.zjvis.datascience.common.dto.user.UserDTO;
import org.zjvis.datascience.common.exception.BaseErrorCode;
import org.zjvis.datascience.common.exception.DataScienceException;
import org.zjvis.datascience.common.model.ApiResultCode;
import org.zjvis.datascience.common.util.JwtUtil;
import org.zjvis.datascience.service.DatasetCategoryService;
import org.zjvis.datascience.service.PipelineService;
import org.zjvis.datascience.service.TaskService;
import org.zjvis.datascience.service.UserProjectService;
import org.zjvis.datascience.service.dataset.DatasetService;
import cn.hutool.core.bean.BeanUtil;

/**
 * @description 权限拦截切面 Aspect
 * @date 2021-09-23
 */
@Component
@Aspect
public class AuthAspect {

    @Autowired
    private UserProjectService userProjectService;

    @Autowired
    private DatasetService datasetService;

    @Autowired
    private DatasetCategoryService datasetCategoryService;

    @Autowired
    private PipelineService pipelineService;

    @Autowired
    private TaskService taskService;

    @Pointcut("execution(* org.zjvis..web.controller.*.*(..)))")
    public void controller() {
    }

    @Pointcut("@within(org.zjvis.datascience.common.annotation.RoleCheck)")
    public void admin() {
    }

    @Before("controller()")
    public void checkAuth(JoinPoint joinPoint) throws Throwable {
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();

        // 所有参数
        Object[] params = joinPoint.getArgs();

        // 获取所有参数上的注解
        Annotation[][] annotations = ms.getMethod().getParameterAnnotations();

        for (int i = 0; i < annotations.length; i++) {
            int j = 0;
            for (; j < annotations[i].length; j++) {
                // 参数 i 的 j 注解
                if ((annotations[i][j] instanceof ProjectAuth)) {
                    checkProjectAuth(params[i], (ProjectAuth) annotations[i][j]);
                    break;
                } else if ((annotations[i][j] instanceof DatasetAuth)) {
                    checkDatasetAuth(params[i], (DatasetAuth) annotations[i][j]);
                    break;
                } else if ((annotations[i][j] instanceof CategoryAuth)){
                    checkCategoryAuth(params[i], (CategoryAuth) annotations[i][j]);
                    break;
                } else if (annotations[i][j] instanceof ProjectRoleAuth){
                    checkProjectRoleAuth(params[i], (ProjectRoleAuth) annotations[i][j]);
                    break;
                } else if (annotations[i][j] instanceof PipelineAuth) {
                    checkPipelineAuth(params[i], (PipelineAuth) annotations[i][j]);
                } else if (annotations[i][j] instanceof TaskAuth) {
                    checkTaskAuth(params[i], (TaskAuth) annotations[i][j]);
                }
            }
        }
    }

    @Before("admin()")
    public void checkRole(JoinPoint joinPoint) throws Throwable {
        UserDTO user = JwtUtil.getCurrentUserDTO();
        if(user.getRole() != null && user.getRole() != 1) {
            throw DataScienceException.of(BaseErrorCode.UNAUTHORIZED, "需要管理员权限！");
        }
    }

    private void checkProjectAuth(Object obj, ProjectAuth projectAuth) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(projectAuth.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, projectAuth.field());
        }
        if (value == null) {
            throw new DataScienceException(ApiResultCode.PARAM_ERROR, "需要传入projectId校验权限!");
        }
        if (value instanceof String) {
            try {
                value = Long.valueOf((String)value);
            } catch (Exception ignored) {
            }
        }
        if (value instanceof Integer) {
            try {
                value = Long.valueOf(value.toString());
            } catch (Exception ignored) {
            }
        }
        if (!(value instanceof Long)) {
            throw new DataScienceException(ApiResultCode.PARAM_ERROR, "项目权限字段配置有误:非长整型.字段名:"
                    + projectAuth.field() + ", value:" + value + ", type is " + value.getClass());
        }
        long projectId = ((Long) value);
        userProjectService.checkAuth(projectId, projectAuth.auth().getValue());
    }

    /**
     * 数据集接口权限验证，只有创建人有读写权限，所以不具体细分，导入project的数据集由projectAuth校验
     * @param obj
     */
    private void checkDatasetAuth(Object obj, DatasetAuth datasetAuth) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(datasetAuth.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, datasetAuth.field());
        }
        if (value == null) {
            return;
        }

        if (!(value instanceof Long)) {
            throw new DataScienceException(ApiResultCode.PARAM_ERROR, "数据集id类型错误:非长整型.字段名:"
                + datasetAuth.field() + ", value:" + value + ", type is " + value.getClass());
        }
        long id = ((Long) value);
        datasetService.checkAuth(id, datasetAuth.checkSystemAdmin());
    }

    /**
     * 数据集分类接口权限验证，只有创建人有读写权限，所以不具体细分
     * @param obj
     * @param categoryAuth
     */
    private void checkCategoryAuth(Object obj, CategoryAuth categoryAuth) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(categoryAuth.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, categoryAuth.field());
        }
        if (value == null) {
            return;
        }

        if (!(value instanceof Long)) {
            throw new DataScienceException(ApiResultCode.PARAM_ERROR, "数据集id类型错误:非长整型.字段名:"
                + categoryAuth.field() + ", value:" + value + ", type is " + value.getClass());
        }
        long categoryId = ((Long) value);
        datasetCategoryService.checkAuth(categoryId);
    }

    private void checkProjectRoleAuth(Object obj, ProjectRoleAuth projectRoleAuth) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(projectRoleAuth.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, projectRoleAuth.field());
        }
        if (value == null) {
            throw new DataScienceException(ApiResultCode.PARAM_ERROR, "需要传入projectId校验权限!");
        }
        if (value instanceof String) {
            try {
                value = Long.valueOf((String)value);
            } catch (Exception ignored) {
            }
        }
        if (value instanceof Integer) {
            try {
                value = Long.valueOf(value.toString());
            } catch (Exception ignored) {
            }
        }
        if (!(value instanceof Long)) {
            throw new DataScienceException(ApiResultCode.PARAM_ERROR, "项目权限字段配置有误:非长整型.字段名:"
                    + projectRoleAuth.field() + ", value:" + value + ", type is " + value.getClass());
        }

        Long projectId = ((Long) value);
        Long userId = JwtUtil.getCurrentUserId();
        userProjectService.checkRoleAuth(projectId,projectRoleAuth.role().getValue(),projectRoleAuth.checkLock(),userId);
    }

    private void checkPipelineAuth(Object obj, PipelineAuth pipelineAuth) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(pipelineAuth.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, pipelineAuth.field());
        }
        if (value == null) {
            throw new DataScienceException(ApiResultCode.PARAM_ERROR, "需要传入pipelineId校验权限!");
        }
        if (value instanceof String) {
            try {
                value = Long.valueOf((String)value);
            } catch (Exception ignored) {
            }
        }
        if (value instanceof Integer) {
            try {
                value = Long.valueOf(value.toString());
            } catch (Exception ignored) {
            }
        }

        long pipelineId = ((Long) value);
        pipelineService.checkAuth(pipelineId);
    }

    private void checkTaskAuth(Object obj, TaskAuth taskAuth) {
        if (obj == null) {
            return;
        }
        Object value;
        if (StringUtils.EMPTY.equals(taskAuth.field())) {
            value = obj;
        } else {
            value = BeanUtil.getFieldValue(obj, taskAuth.field());
        }
        if (value == null) {
            throw new DataScienceException(ApiResultCode.PARAM_ERROR, "需要传入taskId校验权限!");
        }
        if (value instanceof String) {
            try {
                value = Long.valueOf((String)value);
            } catch (Exception ignored) {
            }
        }
        if (value instanceof Integer) {
            try {
                value = Long.valueOf(value.toString());
            } catch (Exception ignored) {
            }
        }

        if (!(value instanceof Long)) {
            throw new DataScienceException(ApiResultCode.PARAM_ERROR, "任务id字段配置有误:非长整型.字段名:"
                    + taskAuth.field() + ", value:" + value + ", type is " + value.getClass());
        }

        long taskId = ((Long) value);
        taskService.checkAuth(taskId);
    }
}
